/* $Header: detector.c,v 2.2 01/10/02 10:26:06 przybyls Exp $ */

/***********************************************************************/
/*                                                                     */
/*                  Copyright Hewlett-Packard Co. 1991                 */
/*                                                                     */
/***********************************************************************/

/***********************************************************************/
/* MODULE NAME:  detector.c                                            */
/* ============                                                        */
/*                                                                     */
/* Author:  Lesley Freed                                               */
/*                                                                     */
/* MODULE PURPOSE:  To initialize the detectors and perform command    */
/*                  system functions.                                  */
/*                                                                     */
/***********************************************************************/

/* global include files */
#include <typedefs.h>
#include <pcb_defs.h>
#include <regs.h>
#include <i_op.h>
#include <iomap.h>
#include <p_wkfile.h>
#include <wkfile.h>
#include <ramps.h>
#include <zone_config.h>
#include <config.h>
#include <zone_status.h>
#include <status.h>
#include <error.h>
#include <detector.h>
#include <p_status.h>
#include <p_ui.h>
#include <exception.h>
#include <err_handler.h>
#include <meth_config.h>

#include <sig_types.h>
#include <det_ui.h>
#include <oven.h>

/* static prototypes */
static void FinishInitializeDetectors(POSITION p);

/***********************************************************************/

void InitializeDetectors(void)
/*
** Called during instrument startup.  Initiates offset measurement
** of detector boards.
*/
{
        /* initialize all the detector input bits to 0 */
        DISCRETE_OUT2 = DiscreteOut2_Image = 0;

        /*
        ** Turn the detector boards ON, disable the detector board
        ** inputs, set the DSP to LINEAR_DETECTOR, and set the DSP
        ** offsets to zero in order to perform the offset measurement.
        ** These are set to normal values when MeasureDetectorOffset() completes.
        **
        ** The offset measurement is meaningless for a TCD, and energizing
        ** the filament in a possibly cold detector may destroy the cell!
        ** So we initialize TCD detectors to OFF:  the TCD state machine
        ** will turn the filament on later if appropriate.
        */
        if (GetDetectorType(FRONT) != TCD)
        {
                SetDetOnOff(FRONT, ON);         /* electrometer on */
                SetDetInput0(FRONT, ON);        /* input disable on */
                Dsp_Out.deta_type = (U_INT16)LINEAR_DETECTOR;
                Dsp_Out.deta_param = 0;         /* DSP offset = 0 */
        }
        else
        {
                SetDetOnOff(FRONT, OFF);        /* filament off */
        }

        if (GetDetectorType(BACK) != TCD)
        {
                SetDetOnOff(BACK, ON);          /* electrometer on */
                SetDetInput0(BACK, ON);         /* input disable on */
                Dsp_Out.detb_type = (U_INT16)LINEAR_DETECTOR;
                Dsp_Out.detb_param = 0;         /* DSP offset = 0 */
        }
        else
        {
                SetDetOnOff(BACK, OFF);         /* filament off */
        }



        if (GetDetectorType(FRONT) == NPD)
        {
           DetSerial[(int)0] = 0; /* to allow slow voltage increase after power up */
        }

        if (GetDetectorType(BACK) == NPD)
        {
           DetSerial[(int)1] = 0; /* to allow slow voltage increase after power up */
        }


        /*
        ** Skip the offset measurement to speed poweron if OC4 (bit 6 of PDR)
        ** is LOW (manufacturing test fixture uses this).
        ** Also skip if DSP load returned errors.
        **
        ** WARNING:  If the instrument has not undergone a "normal" poweron
        **           at least once since the current detector boards were
        **           installed then the DSP data stream will be garbage if
        **           the offset measurement is skipped!!!
        */
        if ( (Skip_Power_On_Test) || ((*PDR & 0x40) == 0) || Det_DSP_Startup_Status )
        {
                /* use the old offset measurement from a previous poweron */
                FinishInitializeDetectors(FRONT);
                FinishInitializeDetectors(BACK);
                UpdateDetStatus();      /* restore LDID setpoints */
                Offset_Measurement_Complete = TRUE;
        }
        else
        {
                /*
                ** Initialize the detector offset data structure: this triggers the actual measurement.
                ** A TCD will go along for the ride, but its measurement result will be ignored.
                */
                DetectorA.offset = 0;
                DetectorB.offset = 0;

                DetectorA.offset_sum = 0;
                DetectorB.offset_sum = 0;

                DetectorA.offset_count = 0;
                DetectorB.offset_count = 0;
        }

        /* Initialize the detector state machines */
        InitDetectorSubtasks();
}

/***********************************************************************/

static void FinishInitializeDetectors(POSITION p)
/*
** Called at end of offset measurement of detector boards to return
** boards to normal operation.
*/
{
        BIT8 on_mask;
        unsigned polarity;
        DET_REC *pDet;
        U_INT16 *pParam, *pType;

        /* setup position-dependent variables */
        if (p == FRONT)
        {
                on_mask      = DETA_BD_ON;
                pDet         = &DetectorA;
                pParam       = &Dsp_Out.deta_param;
                pType        = &Dsp_Out.deta_type;
                polarity     = Active_Wkfile.det_flags.flag.deta_polarity;
        }
        else
        {
                on_mask      = DETB_BD_ON;
                pDet         = &DetectorB;
                pParam       = &Dsp_Out.detb_param;
                pType        = &Dsp_Out.detb_type;
                polarity     = Active_Wkfile.det_flags.flag.detb_polarity;
        }

        /* Set the detector type (was forced to LINEAR_DETECTOR for offset measurement). */
        *pType = (U_INT16)(pDet->type);

        if (GetDetectorType(p) == TCD)
        {
                /* Send TCD polarity to DSP */
                *pParam = polarity;
        }
        else
        {
                /* Send (average_offset/16) to DSP */
                *pParam = pDet->offset >> 4;

                /* Set the electronics ON/OFF state (was forced to ON for offset measurement) */
                SetDetOnOff(p, (Active_Wkfile.detectors_on_off & on_mask) ? ON : OFF);

                /* Enable the Detector input (was disabled for offset measurement). */
                SetDetInput0(p, OFF);   /* input disable off */
        }

        F_LDID_Plasma = OFF;
        B_LDID_Plasma = OFF;
}

/***********************************************************************/

/* DetectorPFRecovery - init detector after power fail recovery is complete */

void DetectorPFRecovery(void)
{
        INT16 p;

        /* re-adjust bead voltage of any NPD detector whose bead setpoint is "on" */
        for (p = 0; p < MAX_DETECTORS; p++)
        {
                if ( (GetDetectorType((POSITION)p) == NPD) && (GetNPDBeadTurnOn((POSITION)p) != OFF) )
                {
                        NPDIgnite((POSITION)p, TRUE);
                }
        }
}

/***********************************************************************/

void SetDetDefaultSetpts(const void *position)
/*
**      Initializes detector setpoints in Active_Wkfile and PF RAM.
**      Called after powerfail memory reset or physical configuration change.
*/
{
        POSITION p;
        BIT8     channl;

        /* cast the parameter through an intermediate type to make flexelint happy */
        p = (POSITION)(int)position;

        /* initialize detector flags to (flame = off), (offset = off), (polarity = normal) */
        /* LDID plasma voltage to ON                                                       */
        if (p == FRONT)
        {
                Active_Wkfile.det_flags.flag.deta_polarity = 1;
                Active_Wkfile.det_flags.flag.deta_flame = 0;
                Active_Wkfile.det_flags.flag.deta_hiv   = 1;
        }
        else
        {
                Active_Wkfile.det_flags.flag.detb_polarity = 1;
                Active_Wkfile.det_flags.flag.detb_flame = 0;
                Active_Wkfile.det_flags.flag.detb_hiv   = 1;
        }

        /* initialize array-type detector setpoints */
        switch (GetDetectorType(p))
        {
        case ECD:
                /* initialize on/off setpoint (pulser) to on */
                (void)SetDetSetptOnOff(p, ON);

                DetUserSerial[(int)p] = ECD_REF_CURRENT_DEFAULT;
                DetSerial[(int)p] = ECDRefCurrentToDAC(ECD_REF_CURRENT_DEFAULT);
                Active_Wkfile.det_target_offset[(int)p] = ECD_TARGET_DEFAULT;
                break;

        case uECD:
                /* initialize on/off setpoint (pulser) to on */
                (void)SetDetSetptOnOff(p, ON);

                DetUserSerial[(int)p] = ECD_REF_CURRENT_DEFAULT;
                DetSerial[(int)p] = ECDRefCurrentToDAC(ECD_REF_CURRENT_DEFAULT);
                break;

        case FID:
                /* initialize on/off setpoint (electrometer) to on */
                (void)SetDetSetptOnOff(p, ON);

                Active_Wkfile.det_lit_offset[(int)p] = FID_LIT_OFFSET_DEFAULT;
                break;

        case FPD:
                /* initialize on/off setpoint (electrometer) to on */
                (void)SetDetSetptOnOff(p, ON);

                Active_Wkfile.det_lit_offset[(int)p] = FPD_LIT_OFFSET_DEFAULT;
                break;

        case NPD:
                /* initialize on/off setpoint (electrometer) to on */
                (void)SetDetSetptOnOff(p, ON);

                /* initialize NPD algorithm parameters (these are common to FRONT and REAR) */
                InitNonvolatileNPD();

                DetSerial[(int)p] = 0;
                Active_Wkfile.npd_bead_voltage[(int)p] = NPD_BEAD_POWER_DEFAULT;
                Active_Wkfile.det_target_offset[(int)p] = NPD_TARGET_DEFAULT;
                Active_Wkfile.det_equib_time[(int)p] = NPD_EQUIB_TIME_DEFAULT;
                break;

        case TCD:
                /* initialize on/off setpoint (filament) to off */
                (void)SetDetSetptOnOff(p, OFF);

                break;

        case LDID:
                /* initialize on/off setpoint  to ON */
                (void)SetLDIDSetptOnOff(p, ON);

                break;

        case OEM_AIB:
        case OEM_FID:
                for (channl=1; channl<= LAST_OEM_ONOFF; channl++)
                {
                      (void)SetOEMCtlOnOff( (POSITION)p, channl, OFF );
                };
                DetSerial[(int)p] = 0;
                LoadDefaultDetSetpts( (POSITION)p  == FRONT ? pFRONT_DETECTOR : pBACK_DETECTOR );
                (void)SetZonePFDefaultSetpts( (POSITION)p  == FRONT ? &Deta_Table: &Detb_Table ) ;
                break;

        default:  /* AIB */
                /* initialize on/off setpoint to on */
                (void)SetDetSetptOnOff(p, ON);
        }
}

void RestoreOEMControls (POSITION p)
{
   U_INT16 channl;
   U_INT16 mask;

   switch ( GetDetectorType(p) )
   {
      case OEM_AIB:
      case OEM_FID:

         for (channl=1; channl<= LAST_OEM_ONOFF; channl++)
         {
            mask = 1 << (channl-1);
            SetOEMDetInput( p, (Active_Wkfile.det_cpf_ctl[p].on_off & mask) == 0 ? OFF : ON , channl);
         };
         DetSerial[ (p == FRONT)? 0:1] = Active_Wkfile.det_cpf_ctl[p].value;
   } ;

}


/***********************************************************************/

/*
** StartDetStateMachines - wake up detector state machines so they reflect
**      Active_Wkfile on/off setpoints.
**
**      WARNING:  this must never be called from a foreground task
**                because it calls SetDetGasState()!
*/
void StartDetStateMachines(void)
{
        INT16 p;
        BIT8 on;
        unsigned on_off;

        for (p = 0; p < MAX_DETECTORS; p++)
        {
                RestoreOEMControls( (POSITION)p );

                switch (GetDetectorType((POSITION)p))
                {
                default:
                case ECD:
                        /* ECD state machine is used only for auto-offset */
                        break;

                case FID:
                case FPD:
                        /* ignite or extinguish flame if setpoint differs from status */
                        on_off = ((POSITION)p == FRONT) ? Active_Wkfile.det_flags.flag.deta_flame
                                                        : Active_Wkfile.det_flags.flag.detb_flame;
                        if ( ((ON_OFF)on_off == ON) && (FlameStatus((POSITION)p) != ON) )
                        {
                                FlameIgnite((POSITION)p);
                        }

                        if ( ((ON_OFF)on_off == OFF) && (FlameStatus((POSITION)p) != OFF) )
                        {
                                FlameExtinguish((POSITION)p);
                        }

                        break;

                case NPD:
                        /* turn bead on or off if setpoint differs from status */
                        on_off = ((POSITION)p == FRONT) ? Active_Wkfile.det_flags.flag.deta_flame
                                                        : Active_Wkfile.det_flags.flag.detb_flame;
                        if ( ((ON_OFF)on_off == ON) && (NPDStatus((POSITION)p) != ON) )
                        {
                                if (NPDStatus((POSITION)p) == OFF)
                                {
                                   NPDIgnite((POSITION)p, FALSE);
                                }
                                else
                                {
                                   /* Restart offset seek if status not ON or OFF */
                                   NPDIgnite((POSITION)p, TRUE);
                                }
                        }

                        if ( ((ON_OFF)on_off == OFF) && (NPDStatus((POSITION)p) != OFF) )
                        {
                                NPDExtinguish((POSITION)p);
                        }

                        break;

                case TCD:
                        /* turn filament on or off if setpoint differs from status */
                        on = ((POSITION)p == FRONT) ? (Active_Wkfile.detectors_on_off & DETA_BD_ON)
                                                    : (Active_Wkfile.detectors_on_off & DETB_BD_ON);
                        if ( on && (TCDStatus((POSITION)p) != ON) )
                        {
                                TCDOn((POSITION)p);
                        }

                        if ( !on && (TCDStatus((POSITION)p) != OFF) )
                        {
                                TCDOff((POSITION)p);
                        }

                        break;

                }
        }
}

/***********************************************************************/

void DetPostOffsetMeasurementInit(void)
/* called after poweron detector offset measurement complete */
{
        /* make detector state machines reflect workfile setpoints */
        StartDetStateMachines();
}

/***********************************************************************/

void UpdateDetStatus(void)
/*
        Called after each load method and at end of diagnostics.
        Updates detector working values to agree with Active_Wkfile setpoints.
*/
{
   POSITION i;

        /* diag cleanup:  reset DSP to normal mode */
        (void)SetDetDiagLinearMode(FRONT, OFF);
        (void)SetDetDiagLinearMode(BACK, OFF);

        /* diag cleanup:  clear det input bit 0 (input disable on ECD/TCD/FID/NPD/AIB/FPD, valve disable on TCD */
        SetDetInput0(FRONT, OFF);
        SetDetInput0(BACK, OFF);

        /* Initialize gas_type-dependent setpoints */
        DetGasTypeChanged(FRONT);
        DetGasTypeChanged(BACK);

        /* TCD needs some special handling relative to the other detectors */
        if (GetDetectorType(FRONT) == TCD)
        {
                /* Initialize TCD polarity setpoint */
                Dsp_Out.deta_param = Active_Wkfile.det_flags.flag.deta_polarity;
        }
        else
        {
                /* update detector on/off for all except TCD:  filament on/off is only set by the state machine */
                INTERRUPTS_OFF
                DiscreteOut1_Image &= DETA_BD_OFF;
                DiscreteOut1_Image |= Active_Wkfile.detectors_on_off & DETA_BD_ON;

                if (GetDetectorType(FRONT) == LDID)
                {
                   if (Active_Wkfile.detectors_on_off & DETA_BD_ON)
                      DiscreteOut2_Image |= DETA_INPUT1_ON;
                   else
                      DiscreteOut2_Image &= DETA_INPUT1_OFF;                  ;

                   if (Active_Wkfile.det_flags.flag.deta_hiv)
                      DiscreteOut2_Image |= DETA_INPUT2_ON;
                   else
                      DiscreteOut2_Image &= DETA_INPUT2_OFF;                  ;
                }
                INTERRUPTS_ON
        }

        if (GetDetectorType(BACK) == TCD)
        {
                /* Initialize TCD polarity setpoint */
                Dsp_Out.detb_param = Active_Wkfile.det_flags.flag.detb_polarity;
        }
        else
        {
                /* update detector on/off for all except TCD:  filament on/off is only set by the state machine */
                INTERRUPTS_OFF
                DiscreteOut1_Image &= DETB_BD_OFF;
                DiscreteOut1_Image |= Active_Wkfile.detectors_on_off & DETB_BD_ON;

                if (GetDetectorType(BACK) == LDID)
                {
                   if (Active_Wkfile.detectors_on_off & DETB_BD_ON)
                      DiscreteOut2_Image |= DETB_INPUT1_ON;
                   else
                      DiscreteOut2_Image &= DETB_INPUT1_OFF;                  ;

                   if (Active_Wkfile.det_flags.flag.detb_hiv)
                      DiscreteOut2_Image |= DETB_INPUT2_ON;
                   else
                      DiscreteOut2_Image &= DETB_INPUT2_OFF;                  ;
                }
                INTERRUPTS_ON
        }

        for (i=FRONT;i<=BACK;i++)
        {
           if (GetDetectorType(i) == uECD)
           {
                INTERRUPTS_OFF
                DetUserSerial[(int)i] = ECD_REF_CURRENT_DEFAULT;
                DetSerial[(int)i] = ECDRefCurrentToDAC(ECD_REF_CURRENT_DEFAULT);
                INTERRUPTS_ON
           }
        }

        /* Force reinitialization of detector state machines. */
        /* Skip if poweron startup, as it will be done after the offset measurement. */
        if (Initialization_Complete)
        {
                StartDetStateMachines();
        }
}

/***********************************************************************/

/* CheckDetConfig - method load config checks for detectors */

void CheckDetConfig(void)
{
  INT16    i;
  DET_TYPE det_type;
  DET_TYPE gccw_type;
  BIT8     hostmodel;

  /* check for detector type mismatch */
  for (i = 0; i < MAX_DETECTORS; i++)
  {
     det_type = GetDetectorType((POSITION)i);
     gccw_type = (DET_TYPE)((BIT8)gccw.DetectorType[i] & (BIT8)0xf);

     switch ( det_type )
     {

      case OEM_AIB:
      case OEM_FID:
        hostmodel =  (i==0) ? DetectorA_Config.host_model : DetectorB_Config.host_model;

        if ((hostmodel==TYPE_13_14_AS_AIB  ) && (gccw_type == AIB   ) ) break ;
        if ((hostmodel==TYPE_13_14_AS_NODET) && (gccw_type == NO_DET) ) break ;

        /* intentional fall-thru */

      default:

        /* NOTE: only look at the detector board ID bits (lower 4 bits).  The  */
        /* rest of the word may contain CPF related information.               */
        if ( det_type !=  gccw_type )
        {
           /* set exception */
           SetNewException((i == 0) ? FRNT_DET_TYPE_MISMATCH : BACK_DET_TYPE_MISMATCH, 0, 0);
        }

        if ( GetElectrometerType((POSITION)i) != gccw.Electrometer[i])
        {
           SetNewException((i == 0) ? FRNT_DET_TYPE_MISMATCH : BACK_DET_TYPE_MISMATCH, 0, 0);
        }
        break ;

     }

  }
}

/***********************************************************************/

/* RTTSetFrntTCDNegPolarity - set TCD polarity working value from run timetable execution */

void RTTSetFrntTCDNegPolarity(INT32 value)
{
        /* 0 == negative polarity, 1 == normal polarity */
        if (GetDetectorType(FRONT) == TCD)
        {
                Dsp_Out.deta_param = ((ON_OFF)value == ON) ? 0 : 1;
        }
}

/***********************************************************************/

/* RTTSetBackTCDNegPolarity - set TCD polarity working value from run timetable execution */

void RTTSetBackTCDNegPolarity(INT32 value)
{
        /* 0 == negative polarity, 1 == normal polarity */
        if (GetDetectorType(BACK) == TCD)
        {
                Dsp_Out.detb_param = ((ON_OFF)value == ON) ? 0 : 1;
        }
}

/***********************************************************************/

/* ResetTCDState - return TCD(s) to Active_Wkfile polarity following run timetable execution */

void ResetTCDState(void)
{
        if (GetDetectorType(FRONT) == TCD)
        {
                Dsp_Out.deta_param = Active_Wkfile.det_flags.flag.deta_polarity;
        }

        if (GetDetectorType(BACK) == TCD)
        {
                Dsp_Out.detb_param = Active_Wkfile.det_flags.flag.detb_polarity;
        }
}

/***********************************************************************/

void DetGasTypeChanged(POSITION p)
/*
 * Called by pneumatics whenever a detector gas is altered.
 */

{
        switch (GetDetectorType(p))
        {
/* REVISIT: do we support this for uECD? */
        case uECD:
        case ECD:
                /* Inform ECD of current gas type */
                /* Note:  the narrow pulse width is only enabled for ArMe.  If
                          the gas type is set to any other or is undefined then
                          use the wider N2 pulse width. */
                switch(DetGasType(p))
                {
                case ArMe:
                        SetDetInput2(p, OFF);   /* Ar/Me = narrow pulse width */
                        break;
                default:
                        SetDetInput2(p, ON);    /* N2 = wide pulse width */
                        break;
                }
                break;
        case TCD:
                /* Inform TCD of current gas type */
                /* Note:  the H2/He setting is used if the gas type is unspecified:
                          H2/He is used for most serious TCD work, and that choice
                          also sets the on-board relay to the de-energized state. */
                switch(DetGasType(p))
                {
                case H2:
                case He:
                case UNKNOWN_GAS:
                        SetDetInput2(p, OFF);   /* H2/He/unspecified */
                        break;
                default:
                        SetDetInput2(p, ON);    /* other */
                        break;
                }
                break;
        }
}

/***********************************************************************/

void DetGasShutdown(POSITION p, INT8 gas)
/*
 * Called by pneumatics whenever a detector gas is shutdown due to a problem.
 */

{
        /*
        ** We ignore the gas parameter because the pneumatics system
        ** silently turns off all the detector gases whenever one of them
        ** is shutdown.  So if this function is called, we can assume that
        ** all the gases for this detector have been turned off
        */
        (void) gas;

        switch (GetDetectorType(p))
        {
        /* for TCD turn off filament */
        case TCD:
                TCDShutdown(p);
                break;

        /* for FID or FPD turn off ignitor */
        case FID:
        case FPD:
                if (GetElectrometerType(p) == FALSE) FlameShutdown(p);
                break;

        /* for NPD turn off bead */
        case NPD:
                NPDShutdown(p);
                break;

        /* no action required for ECD, LDID */
        case uECD:
        case ECD:
        case LDID:
                break;
        }
}

/***********************************************************************/

INT16 IsDetGasRequired(POSITION p, INT8 gas)
/*
** Returns TRUE if detector in position "p" is ON and requires "gas" to
** be ready.
*/
{
        switch (GetDetectorType(p))
        {
        case ECD:
        case uECD:
                /* no gases are required:  some customers like to run without anode purge */
                /*               REVISIT:  uECD doesn't use anode purge.                  */
                break;
        case FID:
                /* if flame ON then require H2 and air */
                if (GetFlame(p) != OFF && (gas == FID_FUEL_GAS || gas == FID_AIR_GAS))
                        return TRUE;
                break;
        case FPD:
                /* if flame ON then require H2 and air */
                if (GetFlame(p) != OFF && (gas == FPD_FUEL_GAS || gas == FPD_AIR_GAS))
                        return TRUE;
                break;
        case NPD:
                /* if bead ON then require H2 and air */
                if (NPDStatus(p) != OFF && (gas == NPD_FUEL_GAS || gas == NPD_AIR_GAS))
                        return TRUE;
                break;
        case TCD:
                /* if filament ON then require reference gas */
                if (TCDStatus(p) != OFF && gas == TCD_REF_GAS)
                        return TRUE;
                break;
        case LDID:
                /* if  ON then require detector gas */
                if (GetDetOnOff(p) != OFF && gas == LDID_DET_GAS)
                        return TRUE;
                break;

        }
        return FALSE;
}

/***********************************************************************/

BIT32 GetDetSerialSetpts(void)
/*
 * Called by serial xfer routine for detector boards.
 *
 * Returns front and rear detector serial interface 16 bit word values
 * packed as [FRNT:REAR] into the 32 bit long return value.
 */
{
        return *(BIT32 *)DetSerial;
}

/***********************************************************************/

BIT16 GetDetControlSetpts( POSITION p )
/*
 * Called by keybaord routine for cpf detector boards.
 */
{
        return DetSerial[ (p == FRONT) ? 0 : 1 ];
}

/***********************************************************************/

void  SetDetControlSetpts( U_INT16 value, POSITION p )
/*
 * Called by keyboard routines for cpf detector boards.
 */
{
     DetSerial[ (p == FRONT) ? 0 : 1 ] = value;
}

/***********************************************************************/
/* FUNCTION:  MeasureDetectorOffset                                    */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:  To determine the detector offset and then perform         */
/*           detector-specific initializations.                        */
/*                                                                     */
/* LIMITATIONS:  Because of the detector-specific initializations      */
/*               this should only be executed once.                    */
/*                                                                     */
/***********************************************************************/

/* 10 sec delay for detector electronics to stabilize = 1000 data points @ 100 Hz */
#define OFFSET_MEASUREMENT_DELAY        1000            /* per Bob Rhodes 14 Dec 92 */

/* valid detector offset range */
#define OFFSET_MIN                      0x30000         /* per Bob Rhodes 28 Mar 94 */
#define OFFSET_MAX                      0xfffff         /* per Bob Rhodes 28 Mar 94 */

void MeasureDetectorOffset(POSITION p)
{
        DET_REC *pDet;
        INT32 *pData;
        DET_TYPE det_config;
        EXCEPTION exception;

        /* setup position-dependent variables */
        if (p == FRONT)
        {
                pDet         = &DetectorA;
                pData        = &Dsp_In.deta_data;
                det_config   = DetectorA_Config.type;
                exception    = FRNT_DET_OFFSET_INVALID;
        }
        else
        {
                pDet         = &DetectorB;
                pData        = &Dsp_In.detb_data;
                det_config   = DetectorB_Config.type;
                exception    = BACK_DET_OFFSET_INVALID;
        }

        pDet->offset_count++;

        if (pDet->offset_count > OFFSET_MEASUREMENT_DELAY)
        {
                /*
                ** The largest reading we can receive from the DSP is 0xff0000 * 33 / 32 = 0x106f800.
                ** To stay within 32 bit unsigned math we sum 128 points, i.e. every other reading out of 256.
                ** This result should not be significantly different from summing every point, because the
                ** DSP filter only updates our "100 Hz" data stream at a 4 Hz rate!
                */

                /* sum only the odd offset readings to get every other point, 128 total */
                if (pDet->offset_count % 2)
                {
                        pDet->offset_sum += *pData;
                }

                /* have we encountered 256 points yet? */
                if (pDet->offset_count >= OFFSET_MEASUREMENT_DELAY + 256)
                {
                        /* Calculate average of the 128 readings.  Multiply by (32/33) to
                           cancel (33/32) gain introduced by DSP 2nd-order noise shaping */
                        pDet->offset = pDet->offset_sum / (33 * 128 / 32);

                        /* offset must be non-zero for this measurement to terminate! */
                        if (pDet->offset == 0)
                        {
                                pDet->offset++;
                        }

                        /* sanity-check the result:  if out-of-range then clamp and notify user */
                        if ( (det_config != TCD) && (det_config != NO_DET) )
                        {
                                if (pDet->offset < OFFSET_MIN)
                                {
                                        pDet->offset = OFFSET_MIN;
                                        SetNewException(exception, 0, 0);
                                }
                                else if (pDet->offset > OFFSET_MAX)
                                {
                                        pDet->offset = OFFSET_MAX;
                                        SetNewException(exception, 0, 0);
                                }
                        }

                        /* return detector to normal operation */
                        FinishInitializeDetectors(p);

                        /*
                        ** if both detector offset measurements are now complete, set semaphore
                        ** to trigger a call to StartDetStateMachines() in StatusMonitor().
                        */
                        if ( (DetectorA.offset != 0) && (DetectorB.offset != 0) )
                        {
                                UpdateDetStatus();      /* restore LDID setpoints */
                                Offset_Measurement_Complete = TRUE;
                        }
                }
        }
}

/* EvalDetectorReadiness - called by ZoneTask at 5.12 Hz rate to re-evaluate detector readiness */

void EvalDetectorReadiness(U_INT16 tym )
{
   INT16 p;

   for (p = 0; p < MAX_DETECTORS; p++)
   {
      /* evaluate readiness of detector state machines */
      EvalDetectorStateReadiness((POSITION)p, tym );
   }
}

/*  InitDetSignalLabel - called by startup.c during power-on and by datacomm */
void InitDetSignalLabel( POSITION position )
{
   if (GetCPFDetPneuPresence( position ))
   {
      (void)SetDetSignalLabel( position, (BIT8 *)&UNKNOWN_LABEL[0] );
   }
   else
   {
      ClearDetSignalLabel( position );
   }
}

/*  InitializeDetConfig - called by startup.c during power-on:  CAUTION: it   */
/*                        must be called after the CPF module eeprom info has */
/*                        been read.                                          */
void InitializeDetConfig( POSITION position )
{
   DET_CONFIG *pconfig;

   pconfig = (position == FRONT)?&DetectorA_Config:&DetectorB_Config;

   /* We don't yet have a signal label for this new detector type so use the  */
   /* default                                                                 */
   InitDetSignalLabel(position);

   /* Use the default title as well */
   ClearElectrometerTitle( position );

   pconfig->electrometer = FALSE;

   /* We are done, so set the old type to our new type */
   /* and set cpf status accordingly.                  */
   pconfig->old_type = pconfig->type;
   pconfig->old_cpf  = GetCPFDetPneuPresence(position);
}

void UpdateDetConfig( void )
{
   DET_CONFIG *pconfig;
   BIT8       cpf;


   pconfig = &DetectorA_Config;
   cpf     = GetCPFDetPneuPresence(FRONT);

   /* Set to factory defaults if CPF status or type has
      changed
   */
   if ((pconfig->type != pconfig->old_type) ||
       (pconfig->old_cpf != cpf ))
   {
      InitializeDetConfig( FRONT );
   }

   InitializeDetBdMode( FRONT );

   if (EpcDetector(pFRONT_DETECTOR)==FALSE)
   {
      InstallElectrometerTitle( FRONT );
   }

   pconfig = &DetectorB_Config;
   cpf     = GetCPFDetPneuPresence(BACK);

   if ((pconfig->type != pconfig->old_type) ||
       (pconfig->old_cpf != cpf ))
   {
      InitializeDetConfig( BACK );
   }

   InitializeDetBdMode( BACK );

   if (EpcDetector(pBACK_DETECTOR)==FALSE)
   {
      InstallElectrometerTitle( BACK );
   }
}

void ResetDetConfig( void )
{
   InitializeDetConfig( FRONT );
   InitializeDetConfig( BACK );

   InitializeDetBdMode( FRONT );
   InitializeDetBdMode( BACK );
}

/*  InitializeDetBdMode - called by startup.c during power-on:  CAUTION: it   */
/*  must be called imediately after the CPF module eeprom info has been read. */
/*  This function updates the electrometer mode of the detector board.  If    */
/*  the detector is EPC it uses the CPF status bit (TRUE if CPF).  If it      */
/*  is not EPC it generally does not update the status (this has to be set    */
/*  by datacomm or keyboard) unless we think it is the slave detector board   */
/*  for a Dual wavelength FPD.                                                */

void InitializeDetBdMode( POSITION position )
{
   DET_CONFIG *pconfig;
   DET_TYPE   type;

   pconfig = ( position == FRONT ) ? &DetectorA_Config : &DetectorB_Config;
   type    = GetDetectorType(position);

   /* Non-FID,FPD */
   if (( type != FID ) && ( type != FPD ))
   {
      pconfig->electrometer = FALSE;
      return;
   }

   /* Is this an EPC detector? */
   if (EpcDetector( (position == FRONT) ? pFRONT_DETECTOR : pBACK_DETECTOR ))
   {
      /* EPC */
      if ( GetCPFDetPneuPresence(position) )
      {
         /* FID is an electrometer */
         pconfig->electrometer = TRUE;
      }
      else
      {
         pconfig->electrometer = FALSE;
      }
   }
   else
   {
      if ( (GetDetectorType((position==FRONT)?BACK:FRONT)==FPD) &&
           (GetZoneInstalled((position==FRONT)?&Deta_Table:&Detb_Table)==FALSE))
      {
         pconfig->electrometer = TRUE;
      }
   }
}

UI_ERR SetDetHostModel ( POSITION position , BIT8 setpt )
{
     if ( setpt > TYPE_13_14_AS_IS ) return INVALID_PARAM;

     if (position == FRONT)
     {
       DetectorA_Config.host_model = setpt;
     }
     else
     {
       DetectorB_Config.host_model = setpt;
     }

     return OK;
}

BIT8   GetDetHostModel ( POSITION position  )
{
     if (position == FRONT)
     {
       return DetectorA_Config.host_model ;
     }
     else
     {
       return DetectorB_Config.host_model ;
     }
}
